package com.gs.pdf.to.word.pdf;

import com.gs.pdf.to.word.model.TextChunk;
import org.apache.pdfbox.contentstream.PDFGraphicsStreamEngine;
import org.apache.pdfbox.cos.COSName;
import org.apache.pdfbox.pdmodel.PDPage;
import org.apache.pdfbox.pdmodel.font.PDFont;
import org.apache.pdfbox.pdmodel.graphics.image.PDImage;
import org.apache.pdfbox.util.Matrix;
import org.apache.pdfbox.util.Vector;

import java.awt.geom.Point2D;
import java.awt.geom.Rectangle2D;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class AdvancedPDFExtractor extends PDFGraphicsStreamEngine {



    public static class ContentItem {
        public final TextChunk text;
        public final byte[] imageBytes;
        public final Rectangle2D rect;

        private ContentItem(TextChunk text, byte[] imageBytes, Rectangle2D rect) {
            this.text = text;
            this.imageBytes = imageBytes;
            this.rect = rect;
        }

        public static ContentItem ofText(TextChunk t) { return new ContentItem(t, null, null); }
        public static ContentItem ofImage(byte[] b) { return new ContentItem(null, b, null); }
        public static ContentItem ofRect(Rectangle2D r) { return new ContentItem(null, null, r); }

        public boolean isText() { return text != null; }
        public boolean isImage() { return imageBytes != null; }
        public boolean isRect() { return rect != null; }
    }

    private final List<ContentItem> content = new ArrayList<>();
    private final StringBuilder lineBuilder = new StringBuilder();
    private float currentX = 0, currentY = 0;
    private float fontSize = 12;
    private PDFont currentFont = null;

    // ✅ 构造函数接收页面
    public AdvancedPDFExtractor(PDPage page) {
        super(page); // 调用父类构造函数
    }

    public List<ContentItem> getContent() {
        flushLine();
        return content;
    }

    private void flushLine() {
        String line = lineBuilder.toString().trim();
        if (!line.isEmpty()) {
            TextChunk chunk = new TextChunk(
                line, currentX, currentY, fontSize,
                isBoldFont(), isItalicFont(), getBaseFontName()
            );
            content.add(ContentItem.ofText(chunk));
            lineBuilder.setLength(0);
        }
    }

    @Override
    protected void showGlyph(Matrix textRenderingMatrix, PDFont font, int code, String unicode, Vector displacement) throws IOException {
        if (textRenderingMatrix != null) {
            currentX = textRenderingMatrix.getTranslateX();
            currentY = textRenderingMatrix.getTranslateY();
        }
        lineBuilder.append(unicode);
        currentFont = font;
        fontSize = getCurrentFontSize();
    }

    @Override
    public void beginText() throws IOException {
        // 开始文本块
    }

    @Override
    public void endText() throws IOException {
        flushLine();
    }

    @Override
    public void drawImage(PDImage pdImage) throws IOException {
        flushLine();
        try (ByteArrayOutputStream imgOs = new ByteArrayOutputStream()) {
            java.awt.image.BufferedImage awtImage = pdImage.getImage();
            if (awtImage != null) {
                javax.imageio.ImageIO.write(awtImage, "png", imgOs);
                byte[] imageData = imgOs.toByteArray();
                if (imageData.length > 50) {
                    content.add(ContentItem.ofImage(imageData));
                }
            }
        } catch (Exception e) {
            System.err.println("图片提取失败: " + e.getMessage());
        }
    }

    @Override
    public void clip(int i) throws IOException {

    }

    @Override
    public void appendRectangle(Point2D p1, Point2D p2, Point2D p3, Point2D p4) throws IOException {
        flushLine();
        double minX = Math.min(Math.min(p1.getX(), p2.getX()), Math.min(p3.getX(), p4.getX()));
        double minY = Math.min(Math.min(p1.getY(), p2.getY()), Math.min(p3.getY(), p4.getY()));
        double maxX = Math.max(Math.max(p1.getX(), p2.getX()), Math.max(p3.getX(), p4.getX()));
        double maxY = Math.max(Math.max(p1.getY(), p2.getY()), Math.max(p3.getY(), p4.getY()));

        Rectangle2D rect = new Rectangle2D.Double(minX, minY, maxX - minX, maxY - minY);
        content.add(ContentItem.ofRect(rect));
    }

    @Override
    public void moveTo(float x, float y) throws IOException {
        flushLine();
    }

    @Override
    public void lineTo(float x, float y) throws IOException {
        flushLine();
    }

    @Override
    public void curveTo(float v, float v1, float v2, float v3, float v4, float v5) throws IOException {

    }

    @Override
    public Point2D getCurrentPoint() throws IOException {
        return null;
    }

    @Override
    public void closePath() throws IOException {
        flushLine();
    }

    @Override
    public void endPath() throws IOException {

    }

    @Override
    public void fillPath(int windingRule) throws IOException {
        flushLine();
    }

    @Override
    public void fillAndStrokePath(int i) throws IOException {

    }

    @Override
    public void shadingFill(COSName cosName) throws IOException {

    }

    @Override
    public void strokePath() throws IOException {
        flushLine();
    }

    private float getCurrentFontSize() {
        try {
            return getGraphicsState().getTextState().getFontSize();
        } catch (Exception e) {
            return 12f;
        }
    }

    private boolean isBoldFont() {
        return currentFont != null && (
            currentFont.getName().toLowerCase().contains("bold") ||
            currentFont.getCOSObject().getNameAsString(COSName.SUBTYPE).toLowerCase().contains("bold")
        );
    }

    private boolean isItalicFont() {
        return currentFont != null && (
            currentFont.getName().toLowerCase().contains("italic") ||
            currentFont.getName().toLowerCase().contains("oblique")
        );
    }

    private String getBaseFontName() {
        if (currentFont == null) return "Unknown";
        return currentFont.getName();
    }

    // ✅ 去掉 processPage，直接使用父类的 processPage()
    // 调用者应直接调用 processPage() 方法
}